﻿Function New-ManifestManagementFolders
{
        [cmdletbinding(SupportsShouldProcess=$True)]
        Param(       
        [Parameter(Mandatory=$true)] 
        [ValidateScript({Test-Path $_ -PathType Container})] 
        [String] 
        $DestinationPath,
        
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
        [PSObject] 
        $DeploymentTarget               
        )
 
    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -Message "Start" -AdditionalData $PSBoundParameters)
    }
    End
    {
        $watch.Stop()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -UniqueId $eventId -Message "End" -AdditionalData @{Duration=$watch.Elapsed})
    }
    Process 
    {
        try
        {
            $ApplicationUniqueKey = Get-Value -Object $DeploymentTarget -Value $DeploymentTarget.Application
            if (-not $ApplicationUniqueKey) {throw "Unable to find the application name"}

            $destinationFilePath = Join-Path -Path $DestinationPath -ChildPath $ApplicationUniqueKey
            if ((Test-Path $destinationFilePath -PathType Container) -ne $true)
            {
                New-Item -Path $destinationFilePath -ItemType Container | Out-Null
            }
        
            $destinationFileName = "FoldersManagement_$($DeploymentTarget.Environment).xml"
            $destinationFilePath = Join-Path -Path $destinationFilePath -ChildPath $destinationFileName

            if (Test-Path -Path $destinationFilePath -PathType Leaf)
            {
                Remove-Item -Path $destinationFilePath -Force 
            }


            $root = New-Object PSObject
            $xmlRoot = [Xml]$root

            $logManagementNode = $xmlRoot.CreateElement("FolderManagement")
            $outNull = $xmlRoot.AppendChild($logManagementNode)

            <#
            $deploymentTargetNode = Write-XmlNode -XmlRoot $xmlRoot -ParentNode $logManagementNode -value $DeploymentTarget -filter {($_.Definition -notlike 'System.Management.Automation.PSCustomObject*') `
                                                                                                             -and ($_.Definition -notlike '*Collection*') `
                                                                                                             -and ($_.Definition -notlike '*Object*')}
            #>
            $DeploymentTarget.Servers.Values | ForEach-Object {
                $server = $_

                $server.Folders | ? {$_.enablePurge -eq $true -or $_.enableArchiving -eq $true} | ForEach-Object {

                   if ($_.enablePurge)
                   {
                       $managedFolder = ConvertTo-ManagedFolder -sourceObject $_ -ManifestServer $server -DeploymentTarget $DeploymentTarget 
                       $managedFolder |Add-Member -MemberType NoteProperty -Name Action -Value "Clean-LogFiles"
                       Write-Debug "New-ManifestManagementFolders : ManagedFolder = $managedFolder"
                       $folderNode = Write-XmlNode -XmlRoot $xmlRoot -ParentNode $logManagementNode -value $managedFolder -filter {($_.Definition -notlike 'System.Management.Automation.PSCustomObject*') `
                                                                                                                                     -and ($_.Definition -notlike '*Collection*') `
                                                                                                                                     -and ($_.Definition -notlike '*Object*')}
                                                                                                                                                                            
                   }
                   
                   if ($_.enableArchiving)
                   {
                       $managedFolder = ConvertTo-ManagedFolder -sourceObject $_ -ManifestServer $server  -DeploymentTarget $DeploymentTarget 
                       $managedFolder |Add-Member -MemberType NoteProperty -Name Action -Value "Get-LogFiles"
                       Write-Debug "New-ManifestManagementFolders : ManagedFolder = $managedFolder"
                       $folderNode = Write-XmlNode -XmlRoot $xmlRoot -ParentNode $logManagementNode -value $managedFolder -filter {($_.Definition -notlike 'System.Management.Automation.PSCustomObject*') `
                                                                                                                                     -and ($_.Definition -notlike '*Collection*') `
                                                                                                                                     -and ($_.Definition -notlike '*Object*')}
                   
                   } 
                }
            }


            $xmlRoot.Save($destinationFilePath)
        }
        catch [Exception]
        {
            Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -UniqueId $eventId -Category Error -Message "Error" -AdditionalData @{Exception=$_}) | Out-Null
            throw
        }
    }
}


Function Invoke-ManagementFoldersActions
{
        [cmdletbinding(SupportsShouldProcess=$True)]
        Param(       
            [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
            [ValidateScript({Test-Path $_ -PathType Container})] 
            [String] 
            $ManifestExploitationManagementDirectory,

            [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
            [String] 
            $ApplicationUniqueKey,

            [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
            [String] 
            $Environment,
            
            [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
            [Validateset("Clean-LogFiles","Get-LogFiles")]
            [String] 
            $Action                         
        )
 
    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -Message "Start" -AdditionalData $PSBoundParameters)
    }
    End
    {
        $watch.Stop()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -UniqueId $eventId -Message "End" -AdditionalData @{Duration=$watch.Elapsed})
    }
    Process 
    {
        try
        {
            
            #Resolve file
            $safeManifestExploitationManagementDirectory = Resolve-Path -Path $ManifestExploitationManagementDirectory

            $destinationFilePath = Join-Path -Path $safeManifestExploitationManagementDirectory.ProviderPath -ChildPath $ApplicationUniqueKey
            $destinationFileName = "FoldersManagement_$Environment.xml"
            $destinationFilePath = Join-Path -Path $destinationFilePath -ChildPath $destinationFileName

            if (-not (Test-Path $destinationFilePath -PathType Leaf))
            {
                throw "Missing file $destinationFilePath"
            }
        
            #Load the xml file of managed folders
            $xmlManifestManagement = Select-Xml -Path $destinationFilePath -XPath "/*"
            $ManifestManagement = New-DynamicObject_v2 -Source $xmlManifestManagement.Node

            $managementActions = @()
            $ManifestManagement.folders | ? {$_.Action -eq $Action} | ForEach-Object {$managementActions += $_}

            if ($managementActions.count -eq 0)
            {
                throw "Management folders file '$destinationFilePath' contains no actions '$Action'"
            }

            $managementActions | ForEach-Object {
                $managedFolder = $_

                Write-Verbose "Folder '$($managedFolder.UniqueKey)' will be managed with following action $Action"
                Invoke-DynamicCmdlet -CmdletName $Action -ManagedObject $managedFolder

            }

        }
        catch [Exception]
        {
            Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -UniqueId $eventId -Category Error -Message "Error" -AdditionalData @{Exception=$_}) | Out-Null
            throw
        }
    }
}




function ConvertTo-ManagedFolder
{
    Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [PSObject] 
        $sourceObject,

        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [PSObject] 
        $ManifestServer,

        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [PSObject] 
        $DeploymentTarget

    )
    Process
    {
        $uniqueKey = Resolve-FullQualifiedUniqueKey -CurrentObject $sourceObject

        #Create the new object model
        $targetObject = New-Object PsObject
        $targetObject | Add-Member -MemberType NoteProperty -Name UniqueKey -Value $uniqueKey
        $targetObject | Add-Member -MemberType NoteProperty -Name ElementType -Value $sourceObject.ElementType
        $targetObject | Add-Member -MemberType NoteProperty -Name Server -Value $server.hostNameFQDN

        #Check Mandatory properties
        Assert-ObjectProperty -SourceObject $sourceObject -ObjectIdentifier $targetObject.uniqueKey -PropertiesName Path
        Assert-ObjectProperty -SourceObject $DeploymentTarget -ObjectIdentifier $DeploymentTarget.uniqueKey -PropertiesName CentralizedManagedFolder, Application, Version

        #source folder Path
        if ($DeploymentTarget.RemoteFolderManagement)
        {
            $SharePath = Get-SharePath -Object $sourceObject
            $checkUncUri=$null
            if ([System.Uri]::TryCreate($SharePath, [System.UriKind]::Absolute, [ref] $checkUncUri) ) {
                if ($checkUncUri.IsUnc)
                {
                    $targetObject | Add-Member -MemberType NoteProperty -Name Source -Value $SharePath
                }
                else
                {
                    throw "Not yet implemented a fallback to administrative share for this folder '$uniqueKey'"
                }
            }
            else
            {
                throw "Unable to identify the share path for this folder '$uniqueKey'"
            }

        }
        else
        {
            $FullPath = Get-FullPath -Object $sourceObject
            $targetObject | Add-Member -MemberType NoteProperty -Name Source -Value $FullPath
        }

        #destination folder path (use to archive target)
        $destinationFolderPath = [System.IO.Path]::Combine($DeploymentTarget.CentralizedManagedFolder, $DeploymentTarget.Application, $DeploymentTarget.Version)
        $destinationFolderPath = [System.IO.Path]::Combine($destinationFolderPath, (Get-Value -Object $sourceObject -Value $sourceObject.Path))
        $targetObject | Add-Member -MemberType NoteProperty -Name Destination -Value $destinationFolderPath

        #rename pattern
        $targetObject | Add-Member -MemberType NoteProperty -Name RenamePattern -Value $server.HostName

        $enableArchiving=$false
        if ($sourceObject.enableArchiving) {$enableArchiving=Get-Value -Object $sourceObject -Value $sourceObject.enableArchiving}
        $targetObject | Add-Member -MemberType NoteProperty -Name EnableArchiving -Value $enableArchiving

        $enablePurge=$false
        if ($sourceObject.enablePurge) {$enablePurge=Get-Value -Object $sourceObject -Value $sourceObject.enablePurge}
        $targetObject | Add-Member -MemberType NoteProperty -Name EnablePurge -Value $enablePurge

        $retentionDays=30
        if ($sourceObject.retentionDays) {$retentionDays=Get-Value -Object $sourceObject -Value $sourceObject.retentionDays}
        $targetObject | Add-Member -MemberType NoteProperty -Name KeepDays -Value $retentionDays

        Write-Output $targetObject
    }
}

Function Get-LogFiles
{ 
    [cmdletbinding(SupportsShouldProcess=$True)]
    Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)] 
        [ValidateScript({Test-Path $_ -PathType Container -ErrorAction Stop})] 
        [String] 
        $source,

        [Parameter(Mandatory=$true,ValueFromPipeline=$false)] 
        [String] 
        $destination,

        [Parameter(Mandatory=$false,ValueFromPipeline=$false)] 
        [String] 
        $RenamePattern,

        [Parameter()]
        [switch]
        $GetErrorFiles
    )
    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -Message "Start" -AdditionalData $PSBoundParameters)
        $errorFiles = @()
    }
    End
    {
        $watch.Stop()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -UniqueId $eventId -Message "End" -AdditionalData @{Duration=$watch.Elapsed})
        if ($GetErrorFiles)
        {
            Write-Output $errorFiles
        }
    }
    Process 
    {

        #Check if TargetedDirectory exist
        if (!(Test-Path -Path $destination))
        {
            Write-Warning "Destination directory doesn't exist : Create $destination"
            New-Item -Path $destination -ItemType Container
        }

        #Read all files from source directory
        $files = Get-ChildItem -Path $source
		if ($files.Length -le 0)
		{
			return
		}

        $countFiles = 0

        #Browse these files
        $files | ForEach-Object {
            
            $countFiles+=1
            Write-Progress -Activity "Get-Logfiles" -Status "In progress" -PercentComplete ($countFiles * 100 / $files.Length)

            
            $copyfileError = $null
            $targetedFilePath = [System.String]::Empty

            Write-Verbose "Detected file : $($_.FullName)"

            if ($RenamePattern)
            {
				$tempFileName = [System.String]::Concat($RenamePattern,"-",$($_.Name))
				$targetedFilePath =Join-Path -Path $destination $tempFileName
            }
            else
            {
                $targetedFilePath =Join-Path -Path $destination $_.Name
            }

            if ($_.GetType().Name -eq 'DirectoryInfo')
            {
                #Reccurese subfolders
                Get-Logiles -source $_.FullName -destination $targetedFilePath
            }
            else
            {


                if (Test-Path -Path $targetedFilePath -PathType Leaf)
                {
                    Write-Verbose "Targeted file $targetedFilePath already exist"
                    $targetedFile = Get-Item -Path $targetedFilePath


                    if (($_.LastWriteTimeUtc -ne $targetedFile.LastWriteTimeUtc) -or ($_.Length -ne $targetedFile.Length))
                    {
                        Write-Verbose "Source file $($_.FullName) is different than $targetedFilePath"                                        
                        Write-Verbose "Copy the file $($_.FullName) to $targetedFilePath"
                        Copy-Item -Path $_.FullName -Destination $targetedFilePath -ErrorAction Continue -Force -ErrorVariable copyfileError
                    }
                    else
                    {
                        Write-Verbose "Source file $($_.FullName) is equal than $targetedFilePath"

                    }

                }
                else
                {
                    Write-Verbose "Targeted file : $targetedFilePath is new"

                    #Targeted File doesn't exist, so copy tthe source
                    Write-Verbose "Copy the file $($_.FullName) to $targetedFilePath"
                    Copy-Item -Path $_.FullName -Destination $targetedFilePath -ErrorAction Continue -Force -ErrorVariable copyfileError

                }

            
                if ($copyfileError)
                {
                    Write-Warning "Error during copy process on $targetedFilePath"

                    $errorFiles += (New-Object -TypeName psobject -Property @{SourcePath=$($_.FullName)
                                                                              Error=$copyfileError})
                }
            }
 
        }#end browsing files

        Write-Progress -Activity "Get-Logfiles" -Status "Complete" -Completed

    }
} 



Function Clean-LogFiles
{ 
    [cmdletbinding(SupportsShouldProcess=$True)]
    Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)] 
        [ValidateScript({Test-Path $_ -PathType Container -ErrorAction Stop})] 
        [String] 
        $source,

        [Parameter(Mandatory=$true,ValueFromPipeline=$false)] 
        [Int32] 
        $KeepDays,

        [Parameter()]
        [switch]
        $GetErrorFiles
    )
    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -Message "Start" -AdditionalData $PSBoundParameters)
        $errorFiles = @()
    }
    End
    {
        $watch.Stop()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -UniqueId $eventId -Message "End" -AdditionalData @{Duration=$watch.Elapsed})
        if ($GetErrorFiles)
        {
            Write-Output $errorFiles
        }
    }
    Process 
    {
        #Recurse read all files from source directory
        $files = Get-ChildItem -Path $source -File -Recurse
		if ($files.Length -le 0)
		{
			return
		}

		
        $countFiles=0
        $oldDate = [System.DateTime]::Today.AddDays(-$KeepDays)

        #Browse these files
        $files | ForEach-Object {

            $countFiles+=1
            Write-Progress -Activity "Clean-Logfiles" -Status "In progress" -PercentComplete ($countFiles * 100 / $files.Length)

            if ($_.LastWriteTimeUtc -lt $oldDate)
            {
                Write-Verbose "delete the file $($_.FullName)"
                Remove-Item -Path $_.FullName -ErrorAction Continue -Force -ErrorVariable removefileError
                
            }
            else
            {
                Write-Verbose "Keep the file $($_.FullName)"
            }


            if ($removefileError)
            {
                Write-Warning "Error during deleting process on $($_.FullName)"

                $errorFiles += (New-Object -TypeName psobject -Property @{SourcePath=$($_.FullName)
                                                                            Error=$removefileError})
            }


        }#end browsing file


        #Read all directory
        $files = Get-ChildItem -Path $source -Directory -Recurse | ? {($_.GetDirectories().count -eq 0) -and ($_.GetFiles().count -eq 0)} | Sort-Object FullName -Descending
        foreach($file in $files)
        {
            Write-Verbose "delete empty directory $($file.FullName)"
            Remove-Item -Path $file.FullName
        }



        Write-Progress -Activity "Clean-Logfiles" -Status "Complete" -Completed


    }
}


Function Invoke-DynamicCmdlet
{
    [cmdletbinding(SupportsShouldProcess=$True)]
    Param(       
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [String] 
        $CmdletName,

        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [PSObject] 
        $ManagedObject                  
    )
 
    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -Message "Start" -AdditionalData $PSBoundParameters)
    }
    End
    {
        $watch.Stop()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -UniqueId $eventId -Message "End" -AdditionalData @{Duration=$watch.Elapsed})
    }
    Process 
    {
        try
        {
            $command = Get-Command $CmdletName
            if (-not $command)
            {
                throw "Command '$CmdletName' doesn't exist in the current scope"
            }

            $commandParameters = @{}

            #TODO or not : Managed Parameterset ???
            $command.Parameters.Values | ForEach-Object {
                $parameter = $_
                $mandatory = $parameter.Attributes | ? {$_.Mandatory -eq $true}

                $parameterValue = $ManagedObject.$($parameter.Name)
                if ((-not $parameterValue) -and $mandatory)
                {
                    throw "Parameter '$($parameter.Name)' is mandatory for action '$Action'. No value found on this object '$($ManagedObject.UniqueKey)'"
                }

                Write-Verbose "Invoke-DynamicCmdlet : Found parameter = '$($parameter.Name)'"
                Write-Verbose "Invoke-DynamicCmdlet : Found parameterValue = '$parameterValue'"


                if ($parameterValue)
                {
                    $commandParameters.Add($parameter.Name,$parameterValue)
                }

            }

            #Create the powershell expression qnd execute it
            Write-Debug "Invoke-DynamicCmdlet : Command parameters = $commandParameters"

            $scriptExpression = "$CmdletName `@commandParameters"
            Write-Debug "Invoke-DynamicCmdlet : scriptExpression = $scriptExpression"
            Invoke-Expression -Command $scriptExpression

        }
        catch [Exception]
        {
            Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -UniqueId $eventId -Category Error -Message "Error" -AdditionalData @{Exception=$_}) | Out-Null
            throw
        }
    }
}

